package com.sdy.healthcode.h5.config;

import com.sdy.common.model.Response;
import com.sdy.common.utils.EncodeUtil;
import com.sdy.common.utils.RandomUtil;
import com.sdy.healthcode.biz.utils.HttpReqUtil;
import com.sdy.mvc.utils.JsonUtil;
import com.sdy.redis.service.RedisService;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

import javax.annotation.PostConstruct;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Data
@Configuration
@PropertySource("file:/opt/config/healthcode_child/wxConfig.properties")
public class WxConfig {
    @Value("${wx.miniapp.appid}")
    private String maAppid;
    @Value("${wx.miniapp.secret}")
    private String maSecret;
    @Value("${wx.offiaccount.appid}")
    private String oaAppid;
    @Value("${wx.offiaccount.secret}")
    private String oaSecret;
    @Value("${wx.config.host.domain}")
    private String configDomain;
    @Autowired
    private ApplicationContext applicationContext;
    
    private static WxConfig wxConfig;
    private static RedisService redisService;
    
    @PostConstruct
    public void init() {
        wxConfig = this;
        redisService = applicationContext.getBean(RedisService.class);
    }

    public interface WxApi {
        String CODE2SESSION = "https://api.weixin.qq.com/sns/jscode2session?appid=${APPID}&secret=${SECRET}&js_code=%s&grant_type=authorization_code";
        String ACCESS_TOKEN = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${APPID}&secret=${SECRET}";
        String JS_API_TICKET = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=${ACCESS_TOKEN}&type=jsapi";
        String OA_CODE_TO_SESSION = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=${APPID}&secret=${SECRET}&code=${CODE}&grant_type=authorization_code";
    }
    
    public interface ReqMethod {
        String GET = "GET";
        String POST = "POST";
    }
    
    public static Map<String, Object> requestMa(String api, String method, Object... params) {
        api = api.replace("${APPID}", wxConfig.getMaAppid()).replace("${SECRET}", wxConfig.getMaSecret());
        String requestUrl = String.format(api, params);
        String resp = "{}";
        if (ReqMethod.GET.equals(method)) {
            resp = HttpReqUtil.get(requestUrl, StandardCharsets.UTF_8.name(), 2000, null);
        } else if (ReqMethod.POST.equals(method)) {
            resp = HttpReqUtil.postJson(requestUrl, "", StandardCharsets.UTF_8.name(), StandardCharsets.UTF_8.name(), 2000, null);
        }
        Map<String, Object> m = JsonUtil.fromJson(resp, HashMap.class);
        checkWxRespStatus(m);
        return m;
    }

    public static Map<String, Object> oaCodeToSession(String code) {
        String requestUrl = WxApi.OA_CODE_TO_SESSION
                .replace("${APPID}", wxConfig.getOaAppid())
                .replace("${SECRET}", wxConfig.getOaSecret())
                .replace("${CODE}", code);
        String resp = HttpReqUtil.get(requestUrl, StandardCharsets.UTF_8.name(), 2000, null);
        Map<String, Object> m = JsonUtil.fromJson(resp, HashMap.class);
        checkWxRespStatus(m);
        return m;
    }
    
    private static void checkWxRespStatus(Map<String, Object> m) {
        if (!m.containsKey("errcode") || (Integer) m.get("errcode") == 0) {
            return;
        }
        throw new RuntimeException(m.get("errmsg") + "(" + m.get("errcode") + ")");
    }

    public static synchronized String getOaAccessToken() {
        String api = wxConfig.getConfigDomain() + "/wxoa/accessToken?timestamp=${TIMESTAMP}&appid=${APPID}&nonce=${NONCE}&signature=${SIGNATURE}";
        String timestamp = System.currentTimeMillis() + "";
        String nonce = RandomUtil.produceString(6);
        String signature = EncodeUtil.sha256(wxConfig.getOaAppid() + "#" + wxConfig.getOaSecret() + "#" + timestamp + "#" + nonce);
        api = api.replace("${APPID}", wxConfig.getOaAppid())
                .replace("${TIMESTAMP}", timestamp)
                .replace("${NONCE}", nonce)
                .replace("${SIGNATURE}", signature);
        Response<String> resp = JsonUtil.fromJson(HttpReqUtil.get(api, StandardCharsets.UTF_8.name(), 2000, null), Response.class);
        if (!resp.getSuccess()) {
            throw new RuntimeException(resp.getMessage());
        }
        return resp.getData();
    }

    public static synchronized String getOaJsApiTicket() {
        String api = wxConfig.getConfigDomain() + "/wxoa/jsApiTicket?timestamp=${TIMESTAMP}&appid=${APPID}&nonce=${NONCE}&signature=${SIGNATURE}";
        String timestamp = System.currentTimeMillis() + "";
        String nonce = RandomUtil.produceString(6);
        String signature = EncodeUtil.sha256(wxConfig.getOaAppid() + "#" + wxConfig.getOaSecret() + "#" + timestamp + "#" + nonce);
        api = api.replace("${APPID}", wxConfig.getOaAppid())
                .replace("${TIMESTAMP}", timestamp)
                .replace("${NONCE}", nonce)
                .replace("${SIGNATURE}", signature);
        Response<String> resp = JsonUtil.fromJson(HttpReqUtil.get(api, StandardCharsets.UTF_8.name(), 2000, null), Response.class);
        if (!resp.getSuccess()) {
            throw new RuntimeException(resp.getMessage());
        }
        return resp.getData();
    }

    /**
     * JS-SDK使用权限签名
     */
    public static Map<String, String> createJsSdkSigniture(String url) {
        String noncestr = RandomUtil.produceStringAndNumber(16);
        String jsapiTicket = getOaJsApiTicket();
        String timestamp = System.currentTimeMillis() / 1000 + "";
        List<Pair<String, String>> paramList = new ArrayList<>();
        paramList.add(Pair.of("noncestr", noncestr));
        paramList.add(Pair.of("jsapi_ticket", jsapiTicket));
        paramList.add(Pair.of("timestamp", timestamp));
        paramList.add(Pair.of("url", url));
        paramList.sort(Comparator.comparing(Pair::getKey));
        String params = paramList.stream().map(item -> item.getKey() + "=" + item.getValue()).collect(Collectors.joining("&"));
        String signature = EncodeUtil.sha1(params);
        Map<String, String> result = new HashMap<>();
        result.put("timestamp", timestamp);
        result.put("noncestr", noncestr);
        result.put("signature", signature);
        result.put("appid", wxConfig.getOaAppid());
        return result;
    }
}
